package com.boarsoft.boar.file.action;

import com.boarsoft.bean.ReplyInfo;
import com.boarsoft.boar.app.AppInstBiz;
import com.boarsoft.boar.common.Constants;
import com.boarsoft.boar.entity.AppInst;
import com.boarsoft.boar.entity.FileInfo;
import com.boarsoft.boar.entity.FileVersion;
import com.boarsoft.boar.file.FileBiz;
import com.boarsoft.boar.file.FileExtService;
import com.boarsoft.boar.file.FileVersionBiz;
import com.boarsoft.boar.service.FileService;
import com.boarsoft.common.Util;
import com.boarsoft.common.dao.PagedResult;
import com.boarsoft.common.util.JsonUtil;
import com.boarsoft.common.util.RandomUtil;
import com.boarsoft.common.util.StringUtil;
import com.boarsoft.config.core.ConfigFileSync;
import com.boarsoft.rpc.core.RpcContext;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Lazy;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

@RestController
@RequestMapping("/file/version")
public class FileVersionAction {
	private static final Logger log = LoggerFactory.getLogger(FileVersionAction.class);

	public static final short PATCH_COLD = 0;
	public static final short PATCH_HOT = 1;

	@Autowired
	private FileBiz fileBiz;
	@Autowired
	private FileVersionBiz fileVersionBiz;
	@Autowired
	private AppInstBiz appInstBiz;
	@Lazy
	@Autowired
	private ConfigFileSync configFileSync;
	@Autowired
	private FileService fileSvc;

	@Autowired
	private FileExtService fileExtService;

	@RequestMapping("/copy.do")
	//@Authorized(code = "file.version.copy")
	public ReplyInfo<String> copy(String id, String env) {
		FileVersion o = fileVersionBiz.copy(id, env);
		return new ReplyInfo<String>(true, JsonUtil.from(o));
	}

	@RequestMapping("/active.do")
	//@Authorized(code = "file.version.active")
	public ReplyInfo<Object> active(String id) {
		fileVersionBiz.active(id);
		return ReplyInfo.SUCCESS;
	}

	@RequestMapping("/list.do")
	//@Authorized(code = "file.version.list")
	public ReplyInfo<Object> list(String fileId, String env, String key, String orderBy, int pageNo, int pageSize) {
		PagedResult<FileVersion> pr = fileVersionBiz.list(fileId, env, key, orderBy, pageNo, pageSize);
		return new ReplyInfo<Object>(true, pr);
	}

	@RequestMapping("/save.do")
	//@Authorized(code = "file.version.save")
	public ReplyInfo<Object> save(FileVersion a, MultipartFile file, String appId) {
		if(file==null||file.isEmpty()||file.getSize()==0){
			return new ReplyInfo<>(false, "上传的文件不能为空！");
		}
		if (Util.strIsEmpty(a.getId())) {
			return this.add(a, file, appId);
		}
		return this.update(a, file, appId);
	}

	@RequestMapping("/add.do")
	//@Authorized(code = "file.version.add")
	public ReplyInfo<Object> add(FileVersion a, MultipartFile file, String appId) {
		a.setId(RandomUtil.genUUID());
		a.setCreateTime(Util.getStdfDateTime());
		a.setLastTime(Util.getStdmfDateTime());
		return this.save(file, a);
	}

	@RequestMapping("/update.do")
	//@Authorized(code = "file.version.update")
	public ReplyInfo<Object> update(FileVersion a, MultipartFile file, String appId) {
		FileVersion o = fileVersionBiz.get(a.getId());
		o.setEnv(a.getEnv());
		o.setMemo(a.getMemo());
		o.setPath(a.getPath());
		o.setVer(a.getVer());
		o.setLastTime(Util.getStdfDateTime());
		fileVersionBiz.update(o);
		return this.save(file, o);
	}

	private ReplyInfo<Object> save(MultipartFile file, FileVersion a) {
		a = fileVersionBiz.add(a);
		if (file == null) {
			return ReplyInfo.SUCCESS;
		}
		// 如果是引用某个远程文件则不处理上传
		String fp = a.getPath();
		if (fp.toLowerCase().startsWith("http")) {
			return ReplyInfo.SUCCESS;
		}
		fp = fileSvc.getFilePath(a);

		boolean b;
		try {
			b = fileExtService.saveFile(fp, file.getBytes());
		} catch (IOException e) {
			log.error("Error on copy uploaded file {} to {}", file, fp, e);
			return Constants.REPLY_ERROR_INTERNAL;
		}
		return b ? new ReplyInfo<Object>(true,a) : Constants.REPLY_ERROR_INTERNAL;
	}

	@RequestMapping("/delete.do")
	//@Authorized(code = "file.version.delete")
	public ReplyInfo<Object> delete(String id) {
		FileVersion o = fileVersionBiz.get(id);
		String fp = fileSvc.getFilePath(o);
		boolean b = fileExtService.deleteFile(fp);
		if (!b){
			return ReplyInfo.FAILED;
		}
		fileVersionBiz.delete(id);
		return ReplyInfo.SUCCESS;
	}

	@RequestMapping("/push.do")
	//@Authorized(code = "file.version.push")
	public ReplyInfo<Object> push(String id, String key) {
		FileVersion o = fileVersionBiz.get(id);
		byte[] ba = null;
		try {
			ba = fileSvc.load(o);
		} catch (IOException e) {
			log.error("Can not load file version {}", o, e);
			return Constants.REPLY_ERROR_INTERNAL;
		}
		FileInfo f = fileBiz.get(o.getFileId());
		Map<String, Object> rm = new HashMap<>();
		if (Util.strIsEmpty(key)) {
			// 向当前应用当前环境所有运行中的实例推送
			List<AppInst> lt = appInstBiz.list(o.getEnv(), f.getAppId(),
					o.getGroup(), AppInst.STATUS_RUNNING);
			for (AppInst a : lt) {
				this.push(a.getAddr(), f, ba, rm);
			}
		} else {
			for (String addr : key.split(",")) {
				this.push(addr, f, ba, rm);
			}
		}
		return new ReplyInfo<>(true, rm);
	}

	protected void push(String addr, FileInfo f, byte[] ba, Map<String, Object> rm) {
		log.info("Sync config {} to app inst {}", f.getCode(), addr);
		RpcContext.specify2(addr);
		try {
			ReplyInfo<Object> ro = configFileSync.syncOne(//
					ba, f.getCode(), f.getDeployPath());
			if (!ro.isSuccess()) {
				log.warn("Sync config {} to app inst {}, reason: {}", //
						f.getCode(), addr, ro.getData());
			}
			rm.put(addr, ro.isSuccess());
		} catch (Exception e) {
			log.error("Sync config {} to app inst {}, reason: {}", //
					f.getCode(), addr, e);
			rm.put(addr, false);
		} finally {
			RpcContext.specify2(null);
		}
	}

	@RequestMapping("/pull.do")
	public ReplyInfo<Object> pull(HttpServletRequest req, HttpServletResponse rsp, Integer port, String code, String key) {
		String ip = req.getRemoteAddr();
		if (port == null) {
			log.warn("{} miss port parameter for pulling config file {}", ip, code);
			return Constants.REPLY_WARN_INVALID;
		}
		String addr = String.format("%s:%d", ip, port);
		log.info("Client {} is pulling file {}", addr, code);
		AppInst ai = appInstBiz.find(ip, port);
		if (ai == null) {
			return Constants.REPLY_ERROR_NOTFOUND;
		}
		try {
			byte[] ba = fileSvc.load(ai.getIp(), ai.getPort(), code);
			this.download(rsp, ba, code);
			return null;
		} catch (IOException e) {
			log.error("Error on seek file {} for {}", addr, e);
			return Constants.REPLY_ERROR_INTERNAL;
		}
	}

	@RequestMapping("/download.do")
	//@Authorized(code = "file.version.download")
	public ReplyInfo<Object> download(HttpServletResponse rsp, String id, String appId, String key) {
		FileVersion a = fileVersionBiz.get(id);
		String fp = fileSvc.getFilePath(a);

		try {
			byte[] ba = fileExtService.getContent(fp);
			this.download(rsp, ba, key);
			return null;
		} catch (Exception e) {
			log.error("Error on dowload stored config file {}", fp, e);
			return Constants.REPLY_ERROR_INTERNAL;
		}
	}
	@RequestMapping("/downloadByFileName.do")
	//@Authorized(code = "file.version.download")
	public ReplyInfo<Object> downloadByFileName(HttpServletResponse rsp, String appCode, String fileName, String env) {
		FileVersion a = fileVersionBiz.getByFileName(appCode,fileName,env);
		String fp = fileSvc.getFilePath(a);

		try {
			byte[] ba = fileExtService.getContent(fp);
			this.download(rsp, ba, null);
			return null;
		} catch (Exception e) {
			log.error("Error on dowload stored config file {}", fp, e);
			return Constants.REPLY_ERROR_INTERNAL;
		}
	}
	@RequestMapping("/downloadByGroup.do")
	//@Authorized(code = "file.version.download")
	public ReplyInfo<Object> downloadByGroup(HttpServletResponse rsp, String appCode, String group, String env) {
		List<FileVersion> fileVersionList = fileVersionBiz.getByGroup(appCode, group, env);
		for(FileVersion f:fileVersionList){
			String fp = fileSvc.getFilePath(f);
			try {
				byte[] ba = fileExtService.getContent(fp);
				this.download(rsp, ba, null);
				return null;
			} catch (Exception e) {
				log.error("Error on dowload stored config file {}", fp, e);
			}
		}
		return null;
	}

	public void download(HttpServletResponse rsp, byte[] ba, String code) throws IOException {
		rsp.setContentType("application/octet-stream");
		rsp.setContentLength(ba.length);
		rsp.addHeader("Content-Disposition", //
				"attachment;filename=".concat(URLEncoder.encode(code, "UTF-8")));
		ServletOutputStream os = rsp.getOutputStream();
		os.write(ba);
		rsp.flushBuffer();
	}



	@RequestMapping("/content.do")
	public ReplyInfo<Object> content(String id) throws UnsupportedEncodingException {
		FileVersion a = fileVersionBiz.get(id);
		String fp = fileSvc.getFilePath(a);
		byte[] ba = fileExtService.getContent(fp);
		return new ReplyInfo<>(true, new String(ba,"UTF-8"));
	}

	@RequestMapping("/rewrite.do")
	//@Authorized(code = "file.version.save")
	public ReplyInfo<Object> rewrite(FileVersion a, String fileContent) {
		if (StringUtil.isBlank(fileContent)) {
			return new ReplyInfo<>(false, "上传的文件不能为空！");
		}
		a.setLastTime(Util.getStdmfDateTime());
		fileVersionBiz.add(a);
		// 如果是引用某个远程文件则不处理上传
		String fp = a.getPath();
		if (fp.toLowerCase().startsWith("http")) {
			return ReplyInfo.SUCCESS;
		}
		fp = fileSvc.getFilePath(a);

		boolean b = fileExtService.saveFile(fp, fileContent.getBytes(StandardCharsets.UTF_8));
		return b ? new ReplyInfo<>(true, a) : Constants.REPLY_ERROR_INTERNAL;
	}
}